/*
author: yiliangwu880
you can get more refer from https://gitee.com/yiliangwu880/svr_util.git
事件，发布订阅 ，publish/subscribe
参考用，具体项目需要新增 多个 EventTraits 定义
特点：泛型编程
	事件接口
	事件ID和事件函数类型绑定一起定义。 写注册代码的时候静态检查，提前发现代码错误。
	channel 由 事件id标识
	用户用起来代码很简洁
	注册事件入口不提供注册对象。 避免对象生存期的控制，减少野指针问题。
	不提供注销
	缺点: 奇技淫巧. 需要修改一些自定义代码：EventTraits
适用:
	多子系统直接调用松耦合。 

code example:
	
	//声明事件参数列表
	----------------------
	template<>
	struct EventTraits<1> {
		using Fun = void(*)(Player &player, int num);
	};	

	//注册事件1回调函数。 为什么放这里：优点，不用去跳别的地方写代码，提高些写代码效率。 缺点，浏览代码不好找到统一注册的地方。
	----------------------
	STATIC_RUN(RegEvent<1>(OnAddMoney);)   -- 可考虑宏 REG_EVENT(1, OnAddMoney)
	void OnAddMoney(Player &player, int num)
	{
		...
	}

	//触发事件1
	----------------------
	
	FireEvent<1>(player, 10);

参考1：
		//为了支持 enum class EVENTID 格式定义
	template<EVENTID ID, class ... Args>
	void FireEvent(Args&& ... args)
	{
		FireEvent<(int)ID>(std::forward<Args>(args)...);
	}

*/

#pragma once

#include <vector>
#include <list>
#include <set>
#include "typedef.h"
#include <functional>
#include <tuple>
#include "../log_file.h"
#include "static_reg.h"

#define REG_EVENT(eventId, fun) STATIC_RUN(RegEvent<(int)eventId>(fun);)  

namespace su
{
	// using PostEventFun = std::function<void(void)>;


	//定义事件ID 关联 接收函数参数列表
	template<int ID>
	struct EventTraits {
		using Fun = void(*)();
	};

	//template<>
	//struct EventTraits<1> {
	//	using Fun = void(*)();
	//};
	//template<>
	//struct EventTraits<2> {
	//	using Fun = void(*)(int i);
	//};
	//template<>
	//struct EventTraits<11> {
	//	using Fun = void(*)();
	//};
	//template<>
	//struct EventTraits<12> {
	//	using Fun = void(*)();
	//};


	template<class Fun>
	struct SubscribeSet  
	{
		std::vector<Fun> m_funs; //订阅channel的回调集合
		bool m_is_firing = false; //true表示触发回调中
	};

	namespace inner//用户不需要访问，不能是匿名。匿名会导致每个cpp文件独立生成不同对象
	{
		//ID标识不同channel
		template<int ID>
		SubscribeSet<typename EventTraits<ID>::Fun> &GetChannel()
		{
			using Fun = typename EventTraits<ID>::Fun;
			static SubscribeSet<Fun> s;
			return s;
		}
	}

	//订阅
	template<const int ID>
	void RegEvent(typename EventTraits<ID>::Fun fun)
	{
		auto &ss = inner::GetChannel<ID>();
		if (ss.m_is_firing)
		{
			su::LogMgr::Ins().Printf(su::LL_ERROR, __FILE__, __LINE__, __FUNCTION__, "can't RegEvent when firing eventId=%lu", ID);
			return;
		}
		ss.m_funs.push_back(fun);
	}

	//发布
	//ID标识channel， 非类型形参 只支持实参为常量
	template<int ID, class ... Args>
	void FireEvent(Args&& ... args)
	{
		auto &ss = inner::GetChannel<ID>();
		if (ss.m_is_firing) //触发回调过程，禁止插入触发，避免复杂递归流程。
		{
			su::LogMgr::Ins().Printf(su::LL_ERROR, __FILE__, __LINE__, __FUNCTION__, "can't recursion trigger");
			return;
		}
		ss.m_is_firing = true;
		for (const auto &v : ss.m_funs)
		{
			v(std::forward<Args>(args)...);
		}
		ss.m_is_firing = false;
	}


} //end namespace su

//用法更像面向对象的写法，以后新项目推荐用这个
namespace su
{
	//定义事件ID 关联 接收函数参数列表
	// template<int ID>
	// struct EventTraits {
	// 	using Fun = void(*)();
	// };

	//template<>
	//struct EventTraits<1> {
	//	using Fun = void(*)();
	//};
	//template<>
	//struct EventTraits<2> {
	//	using Fun = void(*)(int i);
	//};
	//template<>
	//struct EventTraits<11> {
	//	using Fun = void(*)();
	//};
	//template<>
	//struct EventTraits<12> {
	//	using Fun = void(*)();
	//};

	template<int ID>
	class GEvent
	{
	public:
		struct SubscribeSet  
		{
			std::vector<typename EventTraits<ID>::Fun> m_funs; //订阅channel的回调集合
			bool m_is_firing = false; //true表示触发回调中
		};

	private:
		static SubscribeSet &GetChannel()
		{
			static SubscribeSet s;
			return s;
		}

	public:
		static void Reg(typename EventTraits<ID>::Fun fun)
		{
			auto &ss = GetChannel();
			if (ss.m_is_firing)
			{
				su::LogMgr::Ins().Printf(su::LL_ERROR, __FILE__, __LINE__, __FUNCTION__, "can't RegEvent when firing");
				return;
			}
			ss.m_funs.push_back(fun);
		}

		template<class ... Args>
		static void Fire(Args&& ... args)
		{
			auto &ss = GetChannel();
			if (ss.m_is_firing) //触发回调过程，禁止插入触发，避免复杂递归流程。
			{
				su::LogMgr::Ins().Printf(su::LL_ERROR, __FILE__, __LINE__, __FUNCTION__, "can't recursion trigger");
				return;
			}
			ss.m_is_firing = true;
			for (const auto &v : ss.m_funs)
			{
				v(std::forward<Args>(args)...);
			}
			ss.m_is_firing = false;
		}
	};
} 
//end file
